package cn.hy.dataSync.utils;

import cn.hy.dataSync.constant.Delimiter;
import com.google.common.base.Joiner;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.jayway.jsonpath.JsonPath;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.IExpressContext;
import com.ql.util.express.instruction.op.OperatorIn;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.List;


@Slf4j
public final class ExpressUtils {

    private static final ExpressRunner EXPRESS_RUNNER = new ExpressRunner();

    private static final ThreadLocal<List<String>> ERROR_LIST = ThreadLocal.withInitial(Lists::newArrayList);

    public static boolean getBoolean(String condition, IExpressContext<String, Object> context) {
        return (Boolean) evaluate(condition, context);
    }

    public static Object evaluate(String expressString, IExpressContext<String, Object> context) {
        List<String> currentErrorList = ERROR_LIST.get();
        try {
            Object result = EXPRESS_RUNNER.execute(expressString, context, currentErrorList, true, false);
            if (currentErrorList != null && !currentErrorList.isEmpty()) {
                throw new RuntimeException(Joiner.on(Delimiter.POUND).join(currentErrorList));
            }
            return result;
        } catch (Throwable throwable) {
            throw new RuntimeException(Throwables.getRootCause(throwable));
        } finally {
            if (currentErrorList != null) {
                currentErrorList.clear();
            }
        }
    }

    public static boolean isValid(String expressString) {
        try {
            EXPRESS_RUNNER.parseInstructionSet(expressString);
            return true;
        } catch (Throwable throwable) {
            log.error("isValid exception", throwable);
            return false;
        }
    }

    public static void clearCache() {
        EXPRESS_RUNNER.clearExpressCache();
    }

    public static ExpressRunner getExpressRunner() {
        return EXPRESS_RUNNER;
    }

    public static class JsonExtract {
        /**
         * 利于 JsonPath 提取字符串中指定 key 对应的值
         *
         * @param json json串
         * @param path key对应的path
         * @return 值
         */
        public static Object extract(String json, String path) {
            if (StringUtils.isBlank(json) || StringUtils.isBlank(path)) {
                return null;
            }
            return JsonPath.read(json, path);
        }
    }

    static {
        if (!EXPRESS_RUNNER.isShortCircuit()) {
            EXPRESS_RUNNER.setShortCircuit(true);
        }
        try {
            //增加类似于SQL的 not in 操作
            EXPRESS_RUNNER.addOperator("nin", new OperatorIn("nin") {
                private static final long serialVersionUID = 9063776438453764211L;

                @Override
                public Object executeInner(Object[] list) throws Exception {
                    if (list[0] == null) {
                        return true;
                    }
                    return !((Boolean) super.executeInner(list));
                }
            });
            //别名
            EXPRESS_RUNNER.addOperatorWithAlias("notin", "nin", null);
            EXPRESS_RUNNER.addOperatorWithAlias("notIn", "nin", null);
            EXPRESS_RUNNER.addOperatorWithAlias("NOTIN", "nin", null);

            //增加json-extract
            EXPRESS_RUNNER.addFunctionOfClassMethod("jsonExtract",
                    JsonExtract.class.getCanonicalName(),
                    "extract",
                    new Class[]{String.class, String.class},
                    null);
        } catch (Exception ex) {
            log.error("addOperator exception, cause:{}", Throwables.getStackTraceAsString(ex));
        }
    }
}
